/**
 * Copyright (C) 2009 Matteo Merli <mm@merlimat.org>
 * 
 * $Id: PxlInteger.java 24 2009-01-19 19:49:05Z matteo.merli $
 * $URL: http://pxl-lang.googlecode.com/svn/trunk/src/main/java/pxl/types/PxlInteger.java $
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pxl.types;

import java.util.Collection;
import java.util.Map;

import pxl.errors.FormatException;
import pxl.errors.InvalidComparison;
import pxl.errors.TypeError;
import pxl.errors.UnsupportedOperand;
import pxl.types.annotations.Exported;

/**
 * Integer type class.
 * 
 * @author Matteo Merli
 */
public class PxlInteger extends PxlObject
{

    private long value;

    public PxlInteger( long value )
    {
        this.value = value;
    }

    @Override
    public String getType()
    {
        return "int";
    }

    @Override
    public String toString()
    {
        return Long.toString( value );
    }

    public static PxlInteger fromString( String repr, int base )
    {
        if ( base == 16 )
            repr = repr.substring( 2 );
        long value;
        try
        {
            value = Long.parseLong( repr, base );
            return new PxlInteger( value );
        }
        catch ( NumberFormatException nfe )
        {
            throw new FormatException( nfe.getMessage() );
        }
    }

    public long getValue()
    {
        return value;
    }

    // // Operators

    @Exported
    @Override
    public PxlString __repr__()
    {
        return new PxlString( toString() );
    }

    @Exported
    @Override
    public PxlString __str__()
    {
        return __repr__();
    }

    @Exported
    @Override
    public PxlObject __add__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
            return new PxlInteger( value + ((PxlInteger) other).value );
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value + ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "+", this, other );
    }

    @Exported
    @Override
    public PxlObject __mul__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
            return new PxlInteger( value * ((PxlInteger) other).value );
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value * ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "*", this, other );
    }

    @Exported
    @Override
    public PxlObject __pow__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
            return new PxlInteger( (long) Math.pow( value,
                    ((PxlInteger) other).value ) );
        else if ( other instanceof PxlFloat )
            return new PxlFloat( Math
                    .pow( value, ((PxlFloat) other).getValue() ) );
        else
            throw new UnsupportedOperand( "pow() or **", this, other );
    }

    @Exported
    @Override
    public PxlObject __sub__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
            return new PxlInteger( value - ((PxlInteger) other).value );
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value - ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "-", this, other );
    }

    @Exported
    @Override
    public PxlObject __truediv__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
            return new PxlFloat( (double) value / ((PxlInteger) other).value );
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value / ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "/", this, other );
    }

    @Exported
    @Override
    public PxlObject __floordiv__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
        {
            long res = (long) Math.floor( (double) value
                    / ((PxlInteger) other).value );
            return new PxlInteger( res );
        }
        else if ( other instanceof PxlFloat )
            return new PxlFloat( Math.floor( value
                    / ((PxlFloat) other).getValue() ) );
        else
            throw new UnsupportedOperand( "/", this, other );
    }

    @Exported
    @Override
    public PxlTuple __divmod__( PxlObject other )
    {
        if ( other instanceof PxlInteger || other instanceof PxlFloat )
        {
            return new PxlTuple( __floordiv__( other ), __mod__( other ) );
        }
        else
            throw new UnsupportedOperand( "divmod", this, other );
    }

    @Exported
    @Override
    public PxlObject __mod__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
        {
            long o = ((PxlInteger) other).value;
            long res = value % o;
            if ( value < 0 || o < 0 )
                res += o;
            return new PxlInteger( res );
        }
        else if ( other instanceof PxlFloat )
        {
            double o = ((PxlFloat) other).getValue();
            double res = value % o;
            if ( value < 0 || o < 0 )
                res += o;
            return new PxlFloat( res );
        }
        else
            throw new UnsupportedOperand( "%", this, other );
    }

    @Override
    @Exported
    public PxlObject __iadd__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
        {
            value += ((PxlInteger) other).value;
            return null;
        }
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value + ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "-=", this, other );
    }

    @Override
    @Exported
    public PxlObject __isub__( PxlObject other )
    {
        if ( other instanceof PxlInteger )
        {
            value -= ((PxlInteger) other).value;
            return null;
        }
        else if ( other instanceof PxlFloat )
            return new PxlFloat( value - ((PxlFloat) other).getValue() );
        else
            throw new UnsupportedOperand( "-=", this, other );
    }

    // / Reverse operators

    @Exported
    public PxlObject __radd__( PxlObject b )
    {
        return b.__add__( this );
    }

    @Exported
    public PxlObject __rand__( PxlObject b )
    {
        return b.__and__( this );
    }

    @Exported
    public PxlObject __rfloordiv__( PxlObject b )
    {
        return b.__floordiv__( this );
    }

    @Exported
    public PxlObject __rlshift__( PxlObject b )
    {
        return b.__lshift__( this );
    }

    @Exported
    public PxlObject __rmod__( PxlObject b )
    {
        return b.__mod__( this );
    }

    @Exported
    public PxlObject __rmul__( PxlObject b )
    {
        return b.__mul__( this );
    }

    @Exported
    public PxlObject __ror__( PxlObject b )
    {
        return b.__or__( this );
    }

    @Exported
    public PxlObject __rpow__( PxlObject b )
    {
        return b.__pow__( this );
    }

    @Exported
    public PxlObject __rrshift__( PxlObject b )
    {
        return b.__rshift__( this );
    }

    @Exported
    public PxlObject __rsub__( PxlObject b )
    {
        return b.__sub__( this );
    }

    @Exported
    public PxlObject __rxor__( PxlObject b )
    {
        return b.__xor__( this );
    }

    @Exported
    public PxlObject __lshift__( PxlObject b )
    {
        if ( b instanceof PxlInteger )
        {
            PxlInteger o = (PxlInteger) b;
            return new PxlInteger( value << o.value );
        }

        return super.__lshift__( b );
    }

    @Exported
    public PxlObject __rshift__( PxlObject b )
    {
        if ( b instanceof PxlInteger )
        {
            PxlInteger o = (PxlInteger) b;
            return new PxlInteger( value >> o.value );
        }

        return super.__lshift__( b );
    }

    // / Booleans

    @Exported
    @Override
    public PxlBoolean __bool__()
    {
        return PxlBoolean.valueOf( value != 0 );
    }

    @Exported
    @Override
    public PxlObject __not__()
    {
        return PxlBoolean.valueOf( value == 0 );
    }

    @Exported
    @Override
    public PxlObject __pos__()
    {
        return this;
    }

    @Exported
    @Override
    public PxlObject __neg__()
    {
        return new PxlInteger( -value );
    }

    @Exported
    @Override
    public PxlObject __invert__()
    {
        return new PxlInteger( ~value );
    }

    @Exported
    @Override
    public PxlObject __abs__()
    {
        return new PxlInteger( Math.abs( value ) );
    }

    // Comparison

    @Exported
    @Override
    public PxlBoolean __eq__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value == ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            return PxlBoolean.False;
        long otherValue = ((PxlInteger) other).value;
        return PxlBoolean.valueOf( value == otherValue );
    }

    @Exported
    @Override
    public PxlBoolean __neq__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value != ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            return PxlBoolean.True;
        long otherValue = ((PxlInteger) other).value;
        return PxlBoolean.valueOf( value != otherValue );
    }

    @Exported
    @Override
    public PxlBoolean __lt__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value < ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            throw new InvalidComparison( this, other );
        long otherValue = ((PxlInteger) other).value;
        return PxlBoolean.valueOf( value < otherValue );
    }

    @Exported
    @Override
    public PxlBoolean __lte__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value <= ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            throw new InvalidComparison( this, other );
        long otherValue = ((PxlInteger) other).value;
        return PxlBoolean.valueOf( value <= otherValue );
    }

    @Exported
    @Override
    public PxlBoolean __gt__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value > ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            throw new InvalidComparison( this, other );
        long otherValue = ((PxlInteger) other).value;
        return PxlBoolean.valueOf( value > otherValue );
    }

    @Exported
    @Override
    public PxlBoolean __gte__( PxlObject other )
    {
        if ( other instanceof PxlFloat )
        {
            return PxlBoolean.valueOf( value >= ((PxlFloat) other).getValue() );
        }
        else if ( !(other instanceof PxlInteger) )
            throw new InvalidComparison( this, other );
        long otherValue = ((PxlInteger) other).value;

        return PxlBoolean.valueOf( value >= otherValue );
    }

    @Exported
    @Override
    public PxlObject __and__( PxlObject other )
    {
        if ( !(other instanceof PxlInteger) )
            throw new TypeError( "unsupported operand type(s) for &: '"
                    + getType() + "' and '" + other.getType() + "'" );

        long otherValue = ((PxlInteger) other).value;
        return new PxlInteger( value & otherValue );
    }

    @Exported
    @Override
    public PxlObject __or__( PxlObject other )
    {
        if ( !(other instanceof PxlInteger) )
            throw new TypeError( "unsupported operand type(s) for |: '"
                    + getType() + "' and '" + other.getType() + "'" );

        long otherValue = ((PxlInteger) other).value;
        return new PxlInteger( value | otherValue );
    }

    @Exported
    @Override
    public PxlObject __xor__( PxlObject other )
    {
        if ( !(other instanceof PxlInteger) )
            throw new TypeError( "unsupported operand type(s) for ^: '"
                    + getType() + "' and '" + other.getType() + "'" );

        long otherValue = ((PxlInteger) other).value;
        return new PxlInteger( value ^ otherValue );
    }

    @Exported
    @Override
    public PxlObject __int__()
    {
        return new PxlInteger( value );
    }

    @Exported
    @Override
    public PxlObject __float__()
    {
        return new PxlFloat( (double) value );
    }

    // Conversions

    public PxlString toHexString()
    {
        return new PxlString( "0x" + Long.toHexString( value ) );
    }

    public PxlString toOctalString()
    {
        return new PxlString( "0o" + Long.toOctalString( value ) );
    }

    public PxlString toBinaryString()
    {
        return new PxlString( "0b" + Long.toBinaryString( value ) );
    }

    @Override
    public int hashCode()
    {
        return (int) (value ^ (value >>> 32));
    }

    @Override
    public boolean equals( Object other )
    {
        if ( other instanceof PxlInteger )
        {
            return value == ((PxlInteger) other).value;
        }
        return false;
    }

    // Attributes

    private static final Map<String, PxlObject> attrs = PxlObject
            .getExportedMethods( PxlInteger.class );

    @Override
    protected PxlObject getAttribute( String name )
    {
        return attrs.get( name );
    }

    @Override
    protected Collection<String> getAttributeNames()
    {
        return attrs.keySet();
    }

}
